﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;

namespace ShimmerConnect
{
    // -------------------------------------------------------------------------------------
    public class ShimmerApplicationSettings
    {
        // holds information on application settings
        // default or loaded from .xml file


        // SHIMMER device specifics
        public int BaudRate;
        public int ReadTimeout;

        // General overall settings
        public bool SaveToFile;
        public string CSVFilePath;

        // SHIMMER will attempt to connect to all these ports
        public string[] SerialPortConnection;
        public string[] SerialPortConnectBtName; // the bluetooth id

        public ShimmerApplicationSettings()
        {
            // constructor - default settings
            BaudRate = 2400;
            ReadTimeout = 1000;

            SaveToFile = true;
            CSVFilePath = "C:\\temp\\shimmer\\";
            SerialPortConnection = new string[3] { "COM11", "COM7", "COM9" }; // TP1, 2, 3
            SerialPortConnectBtName = new string[] { "1D8F", "1EF6", "20A3" }; // TP1, 2, 3
        }

        public ShimmerApplicationSettings(string pSettingsFilePath)
        {
            // constructor - load settings from file

            XmlReader profileXmlReader = XmlReader.Create(pSettingsFilePath);
            profileXmlReader.Read();
            profileXmlReader.ReadStartElement("ShimmerConnectProfileSettings");
            //profileXmlReader.ReadStartElement("Settings");
            profileXmlReader.ReadToFollowing("Settings");
            profileXmlReader.ReadStartElement("Settings");

            BaudRate = xmlParse.readInt(profileXmlReader, "BaudRate");
            ReadTimeout = xmlParse.readInt(profileXmlReader, "ReadTimeout");
            SaveToFile = xmlParse.readBool(profileXmlReader, "SaveToFile");

            CSVFilePath = xmlParse.readString(profileXmlReader, "CSVFilePath");

            string serialPortConnectionTemp;
            serialPortConnectionTemp = xmlParse.readString(profileXmlReader, "SerialPortConnection");
            SerialPortConnection = serialPortConnectionTemp.Split(',');

            serialPortConnectionTemp = xmlParse.readString(profileXmlReader, "SerialPortConnectBtName");
            SerialPortConnectBtName = serialPortConnectionTemp.Split(',');

            profileXmlReader.ReadEndElement();
            profileXmlReader.Close();
        }

        public string GetSerialPortConnection()
        {
            string outStr = "";

            foreach (string portConn in SerialPortConnection)
            {
                outStr += portConn + ",";
            }

            return outStr.TrimEnd(',');
        }

        public string GetSerialPortConnectionBt()
        {
            string outStr = "";

            foreach (string portConn in SerialPortConnectBtName)
            {
                outStr += portConn + ",";
            }

            return outStr.TrimEnd(',');
        }

        public void SetCsvPath(string pCsvPath)
        {
            CSVFilePath = pCsvPath;
        }
    }

    // -------------------------------------------------------------------------------------
    public class ShimmerCalibration
    { 
        // methods for calibration loading from .xml file

        public class AccelCalibration
        {
            // variables from file
            string btId;
            float xplus, xminus, yplus, yminus, zplus, zminus;

            // calibration variables
            float xMidCorr, yMidCorr, zMidCorr; // midpoint correction
            float xSenCorr, ySenCorr, zSenCorr; // sensitivity correction


            public AccelCalibration(string pBtid, float[] calValues)
            {
                btId = pBtid;
                xplus = calValues[0];
                xminus = calValues[1];
                yplus = calValues[2];
                yminus = calValues[3];
                zplus = calValues[4];
                zminus = calValues[5];
            }

            public AccelCalibration(XmlReader profileXmlReader, string pbtId)
            { 
                // expects profileXmlReader to be advanced to right where the btId tag begins
                btId = pbtId;
                xplus = xmlParse.readFloat(profileXmlReader, "Xplus");
                xminus = xmlParse.readFloat(profileXmlReader, "Xminus");
                yplus = xmlParse.readFloat(profileXmlReader, "Yplus");
                yminus = xmlParse.readFloat(profileXmlReader, "Yminus");
                zplus = xmlParse.readFloat(profileXmlReader, "Zplus");
                zminus = xmlParse.readFloat(profileXmlReader, "Zminus");
            }

            public void ParameterCalc(int specOffset, int specSensitivity)
            {
                // X' = 2048 + [(X-m)*s]
                // 2048 is specOffset

                // m
                xMidCorr = ((xplus + xminus) / 2) - specOffset;
                yMidCorr = ((yplus + yminus) / 2) - specOffset;
                zMidCorr = ((zplus + zminus) / 2) - specOffset;

                // s
                xSenCorr = specSensitivity / (xplus - xminus);
                ySenCorr = specSensitivity / (yplus - yminus);
                zSenCorr = specSensitivity / (zplus - zminus);
            }
        }

        int accelOffset;
        int accelSensitivity;
        AccelCalibration[] accelCalibration;

        public ShimmerCalibration(string pCalibrationFilePath, ShimmerApplicationSettings pSettings)
        {
            // constructor 
            // Set up
            int deviceCount = pSettings.SerialPortConnectBtName.Length;
            string btIdTemp;

            // load settings from file
            XmlReader profileXmlReader = XmlReader.Create(pCalibrationFilePath);
            profileXmlReader.Read();
            profileXmlReader.ReadStartElement("ShimmerDeviceCalibration");

            // Acceleration, general specifications           
            profileXmlReader.ReadToFollowing("AccelSpecs");
            profileXmlReader.ReadStartElement("AccelSpecs");

            accelOffset = xmlParse.readInt(profileXmlReader, "Offset");
            accelSensitivity = xmlParse.readInt(profileXmlReader, "Sensitivity");

            // Acceleration, specifics
            accelCalibration = new AccelCalibration[deviceCount];
            for (int i = 0; i < deviceCount; i++)
            {
                btIdTemp = "RN42-" + pSettings.SerialPortConnectBtName[i];
                profileXmlReader.ReadToFollowing(btIdTemp);
                profileXmlReader.ReadStartElement(btIdTemp);
                accelCalibration[i] = new AccelCalibration(profileXmlReader, pSettings.SerialPortConnectBtName[i]);
                accelCalibration[i].ParameterCalc(accelOffset, accelSensitivity);
            }

            // Close XML
            profileXmlReader.ReadEndElement();
            profileXmlReader.Close();
        }
    }

    // -------------------------------------------------------------------------------------
    public class AppSettings
    {
        public class ShimmerProfileText
        {
            // a 'text' version of the SHIMMER profile. 
            // used primarily to save the profile to harddrive

            public Shimmer.SamplingRates samplingRate; // should be: 0, 10, 50, 100, 125, 166, 200, 250, 500, 1000

            public bool accelActive;
            public bool gyroActive;
            public bool magActive;
            public bool ecgActive;
            public bool emgActive;
            public bool gsrActive;
            public bool anex0Active;
            public bool anex7Active;

            public bool reg5Volt;
            public bool monVolt;

            public Shimmer.AccelSensitivity senAccelSensitivity; // should be: 1.5, 2, 4, 6

            public ShimmerProfileText()
            {
                // constructor
                // the following are a set of default values
                samplingRate = Shimmer.SamplingRates.Sampling100Hz;

                accelActive = true;
                gyroActive = false;
                magActive = false;
                ecgActive = false;
                emgActive = false;
                gsrActive = false;
                anex0Active = false;
                anex7Active = false;

                reg5Volt = false;
                monVolt = false;

                senAccelSensitivity = Shimmer.AccelSensitivity.RANGE_1_5G;
            }

            //public ShimmerProfileText(int psamplingRate, bool[] psenArray, bool[] pregArray, double pAccelSen)
            //{
            //    // constructor
            //    // can opt to use non-default values - used when loading from file

            //    samplingRate = psamplingRate;

            //    accelActive = psenArray[0];
            //    gyroActive = psenArray[1];
            //    magActive = psenArray[2];
            //    ecgActive = psenArray[3];
            //    emgActive = psenArray[4];
            //    gsrActive = psenArray[5];
            //    anex0Active = psenArray[6];
            //    anex7Active = psenArray[7];

            //    reg5Volt = pregArray[0];
            //    monVolt = pregArray[1];

            //    senAccelSensitivity = pAccelSen;
            //}

            public ShimmerProfileText(ShimmerProfile pProfile)
            {
                // constructor
                // transforms a given ShimmerProfile and turn it into text 

                SplitSensors(pProfile.GetSensors());

                if (Enum.IsDefined(typeof(Shimmer.SamplingRates), pProfile.GetAdcSamplingRate()))
                    samplingRate = (Shimmer.SamplingRates)Enum.Parse(typeof(Shimmer.SamplingRates), pProfile.GetAdcSamplingRate().ToString());
                else
                    samplingRate = Shimmer.SamplingRates.Sampling100Hz; // default the value if messed up

                if (Enum.IsDefined(typeof(Shimmer.AccelSensitivity), pProfile.GetAccelSensitivity()))
                    senAccelSensitivity = (Shimmer.AccelSensitivity)Enum.Parse(typeof(Shimmer.AccelSensitivity), pProfile.GetAccelSensitivity().ToString());
                else
                    senAccelSensitivity = Shimmer.AccelSensitivity.RANGE_1_5G; // default the value if messed up

                reg5Volt = false;
                monVolt = false;
            }

            public ShimmerProfileText(string filePath)
            {
                // load profile from file
                XmlReader profileXmlReader = XmlReader.Create(filePath);
                profileXmlReader.Read();
                profileXmlReader.ReadStartElement("ShimmerConnectProfileSettings");
                profileXmlReader.ReadStartElement("Profile");
                samplingRate = (Shimmer.SamplingRates)Enum.Parse(typeof(Shimmer.SamplingRates), xmlParse.readString(profileXmlReader, "SamplingRate"));

                accelActive = xmlParse.readBool(profileXmlReader, "accelActive");
                gyroActive = xmlParse.readBool(profileXmlReader, "gyroActive");
                magActive = xmlParse.readBool(profileXmlReader, "magActive");
                ecgActive = xmlParse.readBool(profileXmlReader, "ecgActive");
                emgActive = xmlParse.readBool(profileXmlReader, "emgActive");
                gsrActive = xmlParse.readBool(profileXmlReader, "gsrActive");
                anex0Active = xmlParse.readBool(profileXmlReader, "anex0Active");
                anex7Active = xmlParse.readBool(profileXmlReader, "anex7Active");

                reg5Volt = xmlParse.readBool(profileXmlReader, "reg5Volt");
                monVolt = xmlParse.readBool(profileXmlReader, "monVolt");

                senAccelSensitivity = (Shimmer.AccelSensitivity)Enum.Parse(typeof(Shimmer.AccelSensitivity), xmlParse.readString(profileXmlReader, "senAccelSensitivity"));

                profileXmlReader.ReadEndElement();
                profileXmlReader.Close();
            }

            public ShimmerProfile GetShimmerProfile()
            {
                // produces a ShimmerProfile version of the variable
                ShimmerProfile pProfile = new ShimmerProfile();

                pProfile.SetAdcSamplingRate((int)samplingRate);

                pProfile.setSensors(combineSensors());
                pProfile.SetVReg(reg5Volt);
                pProfile.SetPMux(monVolt);

                pProfile.SetAccelSensitivity((int)senAccelSensitivity);

                return pProfile;
            }

            private int combineSensors()
            {
                int val = 0;

                if (accelActive)
                    val |= (int)Shimmer.SensorBitmap.SensorAccel;
                if (gyroActive)
                    val |= (int)Shimmer.SensorBitmap.SensorGyro;
                if (magActive)
                    val |= (int)Shimmer.SensorBitmap.SensorMag;
                if (ecgActive)
                    val |= (int)Shimmer.SensorBitmap.SensorECG;
                if (emgActive)
                    val |= (int)Shimmer.SensorBitmap.SensorEMG;
                if (gsrActive)
                    val |= (int)Shimmer.SensorBitmap.SensorGSR;
                if (anex0Active)
                    val |= (int)Shimmer.SensorBitmap.SensorAnExA0;
                if (anex7Active)
                    val |= (int)Shimmer.SensorBitmap.SensorAnExA7;

                return val;
            }

            private void SplitSensors(int val)
            {
                if ((val & (int)Shimmer.SensorBitmap.SensorAccel) != 0)
                    accelActive = true;
                else
                    accelActive = false;

                if ((val & (int)Shimmer.SensorBitmap.SensorGyro) != 0)
                    gyroActive = true;
                else
                    gyroActive = false;

                if ((val & (int)Shimmer.SensorBitmap.SensorMag) != 0)
                    magActive = true;
                else
                    magActive = false;

                if ((val & (int)Shimmer.SensorBitmap.SensorECG) != 0)
                    ecgActive = true;
                else
                    ecgActive = false;

                if ((val & (int)Shimmer.SensorBitmap.SensorEMG) != 0)
                    emgActive = true;
                else
                    emgActive = false;

                if ((val & (int)Shimmer.SensorBitmap.SensorGSR) != 0)
                    gsrActive = true;
                else
                    gsrActive = false;

                if ((val & (int)Shimmer.SensorBitmap.SensorAnExA0) != 0)
                    anex0Active = true;
                else
                    anex0Active = false;

                if ((val & (int)Shimmer.SensorBitmap.SensorAnExA7) != 0)
                    anex7Active = true;
                else
                    anex7Active = false;
            }
        }

        private string filePathProfile = "c:\\temp\\shimmerprofile.xml";
        private string filePathCalibration = "c:\\temp\\shimmercalibration.xml";
        private ShimmerProfile pProfile;
        private ShimmerApplicationSettings pSettings;
        private ShimmerCalibration pCalibration;

        public AppSettings(string pFilePathProfile, string pFilePathCalibration)
        {
            // constructor - profile path is externally set
            filePathProfile = pFilePathProfile;
            filePathCalibration = pFilePathCalibration;

            // check to see if the profile file exist
            if (!File.Exists(filePathProfile))
            {
                // if doesn't exist...
                pProfile = new ShimmerProfile();
                pSettings = new ShimmerApplicationSettings();

                // save the default
                ProfileSave(filePathProfile, pProfile, pSettings);
            }
            else
            {
                // read in the profile
                ShimmerProfileText pProfileT = new ShimmerProfileText(filePathProfile);
                pSettings = new ShimmerApplicationSettings(filePathProfile);
                pProfile = pProfileT.GetShimmerProfile();
            }

            // force update from local on pprofile
            //pProfile.changeSamplingRate = true;
            //pProfile.changeSensors = true;
            //pProfile.change5Vreg = true;
            //pProfile.changePwrMux = true;
            //pProfile.changeAccelSens = true;

            pCalibration = new ShimmerCalibration(filePathCalibration, pSettings);
        }

        public void UpdateAppSettings(ShimmerProfile ppProfile, string pCsvFilePath)
        {
            // update and save new profiles

            pProfile = ppProfile;
            pSettings.SetCsvPath(pCsvFilePath);

            ProfileSave(filePathProfile, pProfile, pSettings);
        }

        public void ProfileSaveXml(string pFilePathProfile, ShimmerProfileText ppProfileT, ShimmerApplicationSettings ppSettings)
        {
            // save the current profile to file

            XmlWriterSettings xmlSettings = new XmlWriterSettings();
            xmlSettings.Indent = true;

            XmlWriter profileXmlWriter = XmlWriter.Create(pFilePathProfile, xmlSettings);// Write Declaration
            profileXmlWriter.WriteStartDocument();

            profileXmlWriter.WriteStartElement("ShimmerConnectProfileSettings"); // start profile

            // Write the root node for Profile
            profileXmlWriter.WriteStartElement("Profile"); // start profile
            //profileXmlWriter.WriteStartAttribute("Profile1");
            //profileXmlWriter.WriteString("Shimmer");
            //profileXmlWriter.WriteEndAttribute();

            XmlElementWriter(profileXmlWriter, "SamplingRate", ppProfileT.samplingRate.ToString());

            XmlElementWriter(profileXmlWriter, "accelActive", ppProfileT.accelActive.ToString());
            XmlElementWriter(profileXmlWriter, "gyroActive", ppProfileT.gyroActive.ToString());
            XmlElementWriter(profileXmlWriter, "magActive", ppProfileT.magActive.ToString());
            XmlElementWriter(profileXmlWriter, "ecgActive", ppProfileT.ecgActive.ToString());
            XmlElementWriter(profileXmlWriter, "emgActive", ppProfileT.emgActive.ToString());
            XmlElementWriter(profileXmlWriter, "gsrActive", ppProfileT.gsrActive.ToString());
            XmlElementWriter(profileXmlWriter, "anex0Active", ppProfileT.anex0Active.ToString());
            XmlElementWriter(profileXmlWriter, "anex7Active", ppProfileT.anex7Active.ToString());

            XmlElementWriter(profileXmlWriter, "reg5Volt", ppProfileT.reg5Volt.ToString());
            XmlElementWriter(profileXmlWriter, "monVolt", ppProfileT.monVolt.ToString());

            XmlElementWriter(profileXmlWriter, "senAccelSensitivity", ppProfileT.senAccelSensitivity.ToString());

            profileXmlWriter.WriteEndElement(); // end profile

            // write the root node for Settings
            profileXmlWriter.WriteStartElement("Settings"); // start settings
            //profileXmlWriter.WriteStartAttribute("Settings");
            //profileXmlWriter.WriteString("Shimmer");
            //profileXmlWriter.WriteEndAttribute();

            XmlElementWriter(profileXmlWriter, "BaudRate", ppSettings.BaudRate.ToString());
            XmlElementWriter(profileXmlWriter, "ReadTimeout", ppSettings.ReadTimeout.ToString());
            XmlElementWriter(profileXmlWriter, "SaveToFile", ppSettings.SaveToFile.ToString());
            XmlElementWriter(profileXmlWriter, "CSVFilePath", ppSettings.CSVFilePath.ToString());
            XmlElementWriter(profileXmlWriter, "SerialPortConnection", ppSettings.GetSerialPortConnection());
            XmlElementWriter(profileXmlWriter, "SerialPortConnectBtName", ppSettings.GetSerialPortConnectionBt());

            profileXmlWriter.WriteEndElement(); // end settings

            profileXmlWriter.WriteEndDocument();
            profileXmlWriter.Flush();
            profileXmlWriter.Close();
        }

        private void XmlElementWriter(XmlWriter pXmlWriter, string pTagString, string pTagValue)
        {
            pXmlWriter.WriteStartElement(pTagString, null);
            pXmlWriter.WriteString(pTagValue);
            pXmlWriter.WriteEndElement();
        }

        public ShimmerProfile GetProfile()
        {
            return pProfile;
        }

        public ShimmerApplicationSettings GetSettings()
        {
            return pSettings;
        }

        public ShimmerCalibration GetCalibration()
        {
            return pCalibration;
        }

        public void ProfileSave(string pFilePathProfile, ShimmerProfile ppProfile, ShimmerApplicationSettings ppSettings)
        {
            // save the profile to file
            ShimmerProfileText profileT = new ShimmerProfileText(ppProfile);
            ProfileSaveXml(pFilePathProfile, profileT, ppSettings);
        }
    }
}
